﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
#if UNITY_EDITOR
using UnityEditor;
[CustomEditor(typeof(Hair))]
public class HairEditor : E_ShowButtons<Hair> { }
#endif
public class Hair : MonoBehaviour
{
    [ShowButton]
    void Snap()
    {
        curveData.SnapBonesToCurve();
    }
    public GenData genData;
    public CurveData curveData;
    public RenderData renderData;
}
public class GenMeshTool
{
    public static bool recalculateNormals = true;
    public static bool recalculateTangents;
    public static bool indep;
    public static bool linkHeadTail;

    public static void GenHair(Hair hair, Material mat, Transform rig)
    {
        var mesh = Generate(hair.genData, true); // 创建网格
        Transform[] bones;
        CreateRig(hair.genData, rig, out bones); // 创建骨架

        var boneWgts = AutoRigging.AutoRigDist(rig, mesh, bones); // 刷权重

        var gs = new Gen_SMR(hair.transform, bones, mesh, rig, mat, boneWgts); // 生成SMR
        gs.Update(); // 先更新骨骼90度旋转造成的影响。
        gs.GenerateSMR();

        var c = hair.curveData; // 添加曲线（修改器）
        c.bones = bones;
        c.GetLengths();

        StackChildsL1(rig);//堆叠骨骼子对象
        var sm = rig.AddComponent<UnityChan.SpringManager>();
        sm.GenSpringBones();
        sm.UpdateParameters();
        sm.dynamicRatio = 0;
    }
    public static void StackChildsL1(Component com)
    {
        var parent = com.transform;
        var childs = parent.GetChildsL1();
        var prev = parent;
        foreach (var child in childs)
        {
            child.SetParent(prev);
            prev = child;
        }
    }
    public static void CreateRig(GenData info, Transform rig, out Transform[] bones)
    {
        ComTool.ClearChildren(rig);
        float totalLength = 0;
        var ls = new float[info.bonesCount];
        float len;
        for (int i = 0; i < info.bonesCount; i++)
        {
            len = info.curveBoneLength.Evaluate1D(i / (float)info.bonesCount);
            totalLength += len;
            ls[i] = len;
        }
        var ratio = info.length / totalLength;
        Transform p = null;
        for (int i = 0; i < info.bonesCount; i++)
        {
            var bone = new GameObject("bone " + i.ToString()).transform;
            bone.parent = p == null ? rig : p;
            p = bone;
            if (i == 0) bone.SetLocalPosY(0);
            else bone.SetLocalPosY(ls[i - 1] * ratio);
        }
        bones = rig.GetTransforms().ToArray();

        var tail = new GameObject("bone tail");
        tail.SetParent(rig);
        tail.SetLocalPosY(info.length);
        tail.SetParent(p, true);

        // flat bones
        foreach (var bone in bones)
        {
            bone.parent = rig;
        }
    }
    static List<Vector3> GetVertsByIndices(Curve2 curve, int sampleBetweenKeys)
    {
        if (curve == null || curve.Count == 0) throw new Exception("null or empty curve");
        var list = new List<Vector3>();
        var factor = 1f / (sampleBetweenKeys - 1);
        Key2 prevKey = null;
        Vector2 v;
        foreach (var key in curve)
        {
            if (prevKey != null)
            {
                for (int i = 0; i < sampleBetweenKeys; i++)
                {
                    v = Curve2.Evaluate(prevKey, key, i * factor);
                    list.Add(v.X0Y() - Vector3.right * 0.5f);
                }
            }
            prevKey = key;
        }
        return list;
    }
    public static Mesh Generate(GenData info, bool reverseNormal = false)
    {
        var mesh = new Mesh();
        mesh.name = "Hair " + DateTime.Now.ToString();

        List<Vector3> vertsShared1, vertsShared2;
        List<Quad> quads1, quads2;
        List<Vector2> uvs1, uvs2;

        var mir = info.curveCrsSecFront.Mirror();
        var vsA = GetVertsByIndices(mir, info.sampleBetweenKeys);
        var vsB = GetVertsByIndices(info.curveCrsSecBack, info.sampleBetweenKeys);

        Generate(info, vsA, out quads1, out vertsShared1, out uvs1); // 头发正面网格
        Quad.ToReverse(quads1);

        Generate(info, vsB, out quads2, out vertsShared2, out uvs2); // 背面
        Quad.Offset(quads2, vertsShared1.Count);

        quads1.AddRange(quads2);
        vertsShared1.AddRange(vertsShared2);
        uvs1.AddRange(uvs2);

        if (reverseNormal)
        {
            foreach (var quad in quads1)
            {
                Quad.ToReverse(quad);
            }
        }
        ApplyMesh(mesh, quads1, vertsShared1, uvs1, null, indep);
        return mesh;
    }
    static void Generate(GenData info, IList<Vector3> vs,
        out List<Quad> quads,
        out List<Vector3> vertsShared,
        out List<Vector2> uvs)
    {
        vertsShared = new List<Vector3>();
        quads = new List<Quad>();
        uvs = new List<Vector2>();

        info.vertsInX = vs.Count;
        var spaceLength = info.length / (info.vertsInY - 1);

        float y;
        Vector3 v;
        for (int i = 0; i < info.vertsInY; i++)
        {
            y = spaceLength * i;
            for (int j = 0; j < info.vertsInX; j++)
            {
                v = vs[j];
                var t = y / info.length;
                v.x *= info.curveWidth.Evaluate1D(t) * info.width;
                v.z *= info.curveThickness.Evaluate1D(t) * info.thickness;
                vertsShared.Add(new Vector3(v.x, y, v.z));
                uvs.Add(new Vector2(v.x, y));
                //uvs.Add(new Vector2(j.IsOdd() ? 1 : 0, i.IsOdd() ? 1 : 0));
                if (i > 0 && j > 0)
                {
                    quads.Add(new Quad(
                            (i - 1) * info.vertsInX + (j - 1),
                            (i - 1) * info.vertsInX + j,
                            i * info.vertsInX + (j - 1),
                            i * info.vertsInX + j));
                }
                if (linkHeadTail && i > 0 && j == info.vertsInX - 1)
                {
                    quads.Add(new Quad(
                    (i - 1) * info.vertsInX + j,
                    (i - 1) * info.vertsInX + 0,
                    i * info.vertsInX + j,
                    i * info.vertsInX + 0));
                }
            }
        }
        var min = HairGen.I.remapMin;
        var max = HairGen.I.remapMax;
        //uvs
        uvs.Remap(min, max);
        //HairGen.I.l = uvs.Clone().Remap(min, max);
        //HairGen.I.uvs = uvs;
    }

    static void ApplyMesh(Mesh mesh, IList<Quad> quads, IList<Vector3> vertsShared,
        IList<Vector2> uvs, IList<Vector3> normsShared = null, bool indep = false)
    {
        var verts = indep ? Quad.ToVertsIndep(quads, vertsShared) : ListTool.IListToArray(vertsShared);
        var indices = Quad.ToIndices(quads, indep);

        mesh.vertices = verts;
        mesh.uv = ListTool.IListToArray(uvs);
        mesh.SetIndices(indices, MeshTopology.Triangles, 0);
        if (normsShared != null)
        {
            mesh.normals = Quad.ToNormals(quads, normsShared, indep);
        }
        if (recalculateNormals) mesh.RecalculateNormals();
        if (recalculateTangents) mesh.RecalculateTangents();
    }
}
